﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using SharpSvn;


namespace OfficeSVN {

    public class Subversion: ISubversion
    {

        /// <summary>
        /// Gets the lock for the passed file.
        /// </summary>
        /// <param name="path"></param>
        public void GetLock(string path) {

            try {
                using (SvnClient client = new SvnClient()) {

                    SvnLockArgs Args = new SvnLockArgs();
                    client.Lock(path, Args);
                }

            } catch (SvnFileSystemOutOfDateException) {

                // Lock failed, because a newer version exists!
                if (MessageBox.Show("Lock failed, because a newer version exists!\r\nDo you want to update and try again?", 
                                    "OfficeSVN - Get lock", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) {

                    // Update the file
                    Update(path);

                    // and try to get the lock again
                    GetLock(path);
                }

            } catch (SvnException exception) {
                HandleSvnException(exception);
            }
        }

        /// <summary>
        /// Release the lock for the passed file.
        /// </summary>
        /// <param name="path"></param>
        public void ReleaseLock(string path) {

            try {
                using (SvnClient client = new SvnClient()) {

                    // Unlock file
                    SvnUnlockArgs Args = new SvnUnlockArgs();
                    client.Unlock(path, Args);
                }

            } catch (SvnException exception) {
                HandleSvnException(exception);
            }
        }

        /// <summary>
        /// Add the passed file to subversion.
        /// </summary>
        /// <param name="path"></param>
        public void AddFile(string path) {

            try {
                using (SvnClient client = new SvnClient()) {

                    SvnAddArgs Args = new SvnAddArgs();
                    client.Add(path, Args);
                }

            } catch (SvnException exception) {
                HandleSvnException(exception);
            }
        }

        /// <summary>
        /// Revert the file and discard the changes.
        /// </summary>
        /// <param name="path"></param>
        public void Revert(string path) {

            try {

                using (SvnClient client = new SvnClient()) {

                    SvnRevertArgs Args = new SvnRevertArgs();
                    client.Revert(path, Args);
                }

            } catch (SvnException exception) {
                HandleSvnException(exception);
            }

        }

        /// <summary>
        /// Update the passed file to the latest revision.
        /// </summary>
        /// <param name="path"></param>
        public void Update(string path) {

            try {
                using (SvnClient client = new SvnClient()) {
                    SvnUpdateArgs Args = new SvnUpdateArgs();
                    client.Update(path, Args);
                }

            } catch (SvnException exception) {
                HandleSvnException(exception);
            }
        }

        public void Commit(string path, string comment) {

            try {
                using (SvnClient client = new SvnClient()) {
                    SvnCommitArgs Args = new SvnCommitArgs();
                    Args.LogMessage = comment;
                    client.Commit(path, Args);
                }

            } catch (SvnException exception) {
                HandleSvnException(exception);
            }
        }

        public bool IsUpdateAvailable(string path) {

            // TODO: At the moment, I have no idea to check if there is an update available.
            //       The code below does not work properly and is only for testing purposes.
            return true;

            //using (SvnClient client = new SvnClient()) {
            //    Uri TargetUri = client.GetUriFromWorkingCopy(path);

            //    SvnTarget LocalTarget = new SvnUriTarget(path);
            //    SvnTarget RemoteTarget = new SvnUriTarget(TargetUri);

            //    Collection<SvnFileVersionEventArgs> Args = new Collection<SvnFileVersionEventArgs>();

            //    // client.Diff(RemoteTarget, out Args);

            //    if (client.DiffSummary(LocalTarget, RemoteTarget, Subversion_SvnDiffSummaryEvent)) {
            //        return true;
            //    }
            //    else {
            //        return false;
            //    }
            //}

            //long LocalRevision = 0;
            //long RemoteRevision = -1;

            //if (_SvnInfoCache.ContainsKey(path)) {
            //    LocalRevision = _SvnInfoCache[path].Revision;
            //}

            //if (LocalRevision == 0) {
            //    return true;
            //}

            //try {
            //    SvnInfoEventArgs Info;

            //    using (SvnClient client = new SvnClient()) {

            //        Uri TargetUri = client.GetUriFromWorkingCopy(path);

            //        SvnUriTarget Target = new SvnUriTarget(TargetUri);

            //        client.GetInfo(Target, out Info);
            //        RemoteRevision = Info.Revision;
            //    }

            //} catch (SvnException exception) {
            //    HandleSvnException(exception);
            //}

            //return (LocalRevision != RemoteRevision);
        }

        public SvnStatus GetStatus(string path) {

            using (SvnClient client = new SvnClient()) {

                // Get the status for the current file.
                System.Collections.ObjectModel.Collection<SvnStatusEventArgs> Statuses = new System.Collections.ObjectModel.Collection<SvnStatusEventArgs>();
                client.GetStatus(path, out Statuses);

                if (Statuses.Count > 0) {
                    return Statuses[0].LocalContentStatus;
                }
            }

            return SvnStatus.None;
        }

        public static void HandleSvnException(SvnException exception) {
            // TODO: Show a readable SvnException
            MessageBox.Show(exception.Message, "Office SVN - Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        public SubversionFileInfo GetInfo(string path)
        {

            SubversionFileInfo returnValue = new SubversionFileInfo {LocalPath = path, Status = SvnStatus.Zero};

            try
            {
                using (SvnClient client = new SvnClient())
                {

                    // Register event, to show the "authentication"-dialog
                    // It seems, that SharpSVN stores his own credentials instead of sharing the credentials with TortoiseSVN.
                    client.Authentication.UserNamePasswordHandlers += Authentication_UserNamePasswordHandlers;

                    Uri documentUri = client.GetRepositoryRoot(path);

                    // Is the file part of a repository?
                    if (documentUri == null)
                    {

                        // Is the path part of a repository?
                        if (client.GetRepositoryRoot(Path.GetDirectoryName(path)) != null)
                        {
                            returnValue.Status = SvnStatus.NotVersioned;
                        }

                        return returnValue;
                    }

                    Uri targetUri = client.GetUriFromWorkingCopy(path);

                    SvnInfoEventArgs info;
                    SvnUriTarget target = new SvnUriTarget(targetUri);

                    // Get the status for the current document.
                    // It seems that this method returns only local status informations. 
                    // This informations are only available, if the document was locally changed.
                    Collection<SvnStatusEventArgs> statuses;
                    client.GetStatus(path, out statuses);

                    if (statuses.Count > 0)
                    {

                        returnValue.Status = statuses[0].LocalContentStatus;
                        returnValue.RepositoryPath = statuses[0].Uri;
                        returnValue.Revision = statuses[0].WorkingCopyInfo.Revision;
                            // oder: Statuses[0].WorkingCopyInfo.LastChangeRevision
                        returnValue.IsLocked = !String.IsNullOrEmpty(statuses[0].WorkingCopyInfo.LockToken);

                    }
                    else
                    {
                        client.GetInfo(target, out info);

                        if (info != null)
                        {
                            returnValue.Status = SvnStatus.None;
                            returnValue.RepositoryPath = info.Uri;
                            returnValue.Revision = info.Revision;
                            returnValue.IsLocked = false;
                            // ReturnValue.NeedsLock = true;
                        }

                        if (returnValue.Status != SvnStatus.Added)
                        {
                            // Check if needs-lock property is set (very slow)
                            string value = "";
                            client.GetProperty(target, "svn:needs-lock", out value);
                            returnValue.NeedsLock = !String.IsNullOrEmpty(value);
                        }
                    }
                }

            }
            catch (SvnException exception)
            {
                Debug.WriteLine(exception);
                returnValue.Status = SvnStatus.Zero;
            }

            return returnValue;
        }

        static void Subversion_SvnDiffSummaryEvent(object sender, SvnDiffSummaryEventArgs e) {
            Console.WriteLine(e.Path);
        }

        static void Authentication_UserNamePasswordHandlers(object sender, SharpSvn.Security.SvnUserNamePasswordEventArgs e) {

            try {
                string Uri = e.RealmUri.AbsoluteUri;
                string UserName = e.UserName;
                string Password = e.Password;

                SvnCredentialsForm CredentialsForm = new SvnCredentialsForm(Uri, UserName, Password);

                if (CredentialsForm.ShowDialog() == DialogResult.OK) {
                    e.UserName = CredentialsForm.Username;
                    e.Password = CredentialsForm.Password;
                    e.Save = CredentialsForm.SaveAuthentication;
                }
            } catch (Exception exception) {
                Debug.WriteLine(exception);
            }
        }

    }
}
